Reading OPUS files

source

This code is modified from the original OPUSreader2 documentation vignettes/opusreader2_introduction.Rmd by Philip Baumann and Thomas Knecht. It reads OPUS binary files and extracts metadata and absorbance data, which can then be plotted.

Plots & Metadata

The main function, read_opus(), reads one or more OPUS files and returns a nested list of class list_opusreader2. Each list contains both the spectral data and metadata for each file. The dsn argument is the data source name. It can be a character vector of folder paths (to read files recursively) or specific OPUS file paths. Start by testing the read_opus function on your corrected spectra folder, and saving the output of the first file.

#load opusreader2 from github.com/spectral-cockpit
library(opusreader2)

#save the path to your corrected spectra
# # Mac path
# corr_spectra <- "/Users/adrianneseiden/Library/CloudStorage/Box-Box/Salk Institute Project/AKS Salk files/Adrianne_FTIRdata/Corrected_files" # nolint
#  # Windows path
# corr_spectra <- "C:/Users/adria/Box/Box-Box/Salk Institute Project/AKS Salk files/Adrianne_FTIRdata/Corrected_files" # nolint
# # Relative path
# corr_spectra <- "../Adrianne_FTIRdata/Corrected_files"

#save the data from your corrected files as 'data_test'
data_test <- read_opus(dsn = corr_spectra)
#check the names of the list
names(data_test)
>  [1] "DRIFTS_pot001_13Cwheat_wk0_root_250725_corr.0"        
>  [2] "DRIFTS_pot012_13Csoy_wk10_root_250827_corr.0"         
>  [3] "DRIFTS_pot029_13Cwheat_wk10_root_250827_corr.0"       
>  [4] "DRIFTS_pot029_13Cwheat_wk40_root_250827_corr.0"       
>  [5] "DRIFTS_pot030_13Csoy_wk10_root_250725_corr.0"         
>  [6] "DRIFTS_pot030_13Csoy_wk40_root_250827_corr.0"         
>  [7] "DRIFTS_pot032_13Cwheat_wk0_root_250725_corr.0"        
>  [8] "DRIFTS_pot047_13Crice_wk0_root_250725_corr.0"         
>  [9] "DRIFTS_pot050_13Csoy_wk0_root_250827_corr.0"          
> [10] "DRIFTS_pot052_13CnoPlant_wk30_root_250919_corr.0"     
> [11] "DRIFTS_pot052_13CnoPlant_wk40_root_250919_corr.0"     
> [12] "DRIFTS_pot053_13Cwheat_wk10_root_250725_corr.0"       
> [13] "DRIFTS_pot053_13Cwheat_wk40_root_250919_corr.0"       
> [14] "DRIFTS_pot055_13Crice_wk10_root_250725_corr.0"        
> [15] "DRIFTS_pot055_13Crice_wk40_root_250827_corr.0"        
> [16] "DRIFTS_pot055qmark_13Crice_wk10_root_250725_corr.0"   
> [17] "DRIFTS_pot056_12Csoy_wk10_root_250919_corr.0"         
> [18] "DRIFTS_pot062_13Csoy_wk0_root_250725_corr.0"          
> [19] "DRIFTS_pot079_13Crice_wk0_root_250725_corr.0"         
> [20] "DRIFTS_pot080_13Csoy_wk10_root_250725_corr.0"         
> [21] "DRIFTS_pot080_13Csoy_wk40_root_250919_corr.0"         
> [22] "DRIFTS_pot086_12Csoy_wk0_root_250919_corr.0"          
> [23] "DRIFTS_pot087_13Crice_wk10_root_250827_corr.0"        
> [24] "DRIFTS_pot087_13Crice_wk40_root_250919_corr.0"        
> [25] "DRIFTS_pot094_13Csoy_wk10_root_250725_corr.0"         
> [26] "DRIFTS_pot094_13Csoy_wk10_soil_250630_corr.0"         
> [27] "DRIFTS_pot094_13Csoy_wk40_root_250919_corr.0"         
> [28] "DRIFTS_pot103_13Crice_wk0_root_recNMR_250725_corr.0"  
> [29] "DRIFTS_pot103_13Crice_wk0_root_vial1_250725_corr.0"   
> [30] "DRIFTS_pot103_13Crice_wk0_root_vial2_250725_corr.0"   
> [31] "DRIFTS_pot103_13Crice_wk0_root_vial3_250725_corr.0"   
> [32] "DRIFTS_pot107_12Crice_wk0_root_really13_250725_corr.0"
> [33] "DRIFTS_pot109_13Cwheat_wk40_root_250919_corr.0"       
> [34] "DRIFTS_pot119_13Crice_wk10_root_250827_corr.0"        
> [35] "DRIFTS_pot119_13Crice_wk40_root_250919_corr.0"
# define 'meas_1' as the first element of the 'data_test' list
meas_1 <- data_test[[1]]

Next I defined a function, plotSpectrum, to plot the spectral data from a single sample (meas_1)

# data is a list containing OPUS file data, including absorbance and metadata.

plotSpectrum <- function(data) { # nolint: object_name_linter.
  ab_data <- data$ab
  if (!is.null(ab_data) &&
        !is.null(ab_data$wavenumbers) && !is.null(ab_data$data) &&
        is.numeric(ab_data$wavenumbers) && is.numeric(ab_data$data) &&
        length(ab_data$wavenumbers) == length(ab_data$data) &&
        all(is.finite(ab_data$wavenumbers)) && all(is.finite(ab_data$data))) {
    plot(
      ab_data$wavenumbers, ab_data$data, type = "l",
      xlab = "Wavenumber (cm⁻¹)", ylab = "Absorbance",
      main = data$basic_metadata$dsn_filename,
      xlim = rev(range(ab_data$wavenumbers))
    )
  } else {
    cat("Absorbance data not found or not valid in this file.\n")
    str(ab_data)
  }
}
plotSpectrum(meas_1)

I then defined the metadataTable function, to extract and display the a subset of the metadata stored in the data extracted by ‘read_opus’ in a table format.

To view all the available data categories, run ‘names(meas_1)’ in the console. To view the parameters within these categories use str(meas_1$category_name) where ‘category_name’ is ‘basic_metadata’, ‘optics’, etc.

The plotMetadata function combines the plot and table for a single spectrum.

plotMetadata <- function(data) { # nolint: object_name_linter.
  plotSpectrum(data)
  metadataTable(data)
}
plotMetadata(meas_1)

Metadata
Parameter Value
File Name DRIFTS_pot001_13Cwheat_wk0_root_250725_corr.0
Timestamp 2025-07-25 20:18:39 GMT-7
Max Y 0.799607157707214
Min Y -0.0012896629050374
Aperture Setting 6 mm
Scanner Velocity 10.0S
Result Spectrum AB
Resolution 4
Sample Scans 400
End Frequency 400
Start Frequency 4000
Duration 364.56884765625
Experiment (method) SALK_soils_method.xpm

Combined plot and table (folder)

The following for loop runs plotMetadata on each spectrum in the data_test list, which contains all the spectra in the folder. It will plot each spectrum and print its metadata below.

for (i in seq_along(data_test)) {
  cat("#### Spectrum", i, ":", names(data_test)[i], "\n\n")
  data_i <- data_test[[i]]
  ab_data <- data_i$ab
  valid <- !is.null(ab_data) &&
    !is.null(ab_data$wavenumbers) && !is.null(ab_data$data) &&
    is.numeric(ab_data$wavenumbers) && is.numeric(ab_data$data) &&
    length(ab_data$wavenumbers) == length(ab_data$data) &&
    all(is.finite(ab_data$wavenumbers)) && all(is.finite(ab_data$data))
  if (valid) {
    plotSpectrum(data_i)
    print(metadataTable(data_i))
  } else {
    cat("Data not valid or missing for this spectrum.\n")
    str(ab_data)
  }
}

Spectrum 1 : DRIFTS_pot001_13Cwheat_wk0_root_250725_corr.0

Metadata
Parameter Value
File Name DRIFTS_pot001_13Cwheat_wk0_root_250725_corr.0
Timestamp 2025-07-25 20:18:39 GMT-7
Max Y 0.799607157707214
Min Y -0.0012896629050374
Aperture Setting 6 mm
Scanner Velocity 10.0S
Result Spectrum AB
Resolution 4
Sample Scans 400
End Frequency 400
Start Frequency 4000
Duration 364.56884765625
Experiment (method) SALK_soils_method.xpm

Spectrum 2 : DRIFTS_pot012_13Csoy_wk10_root_250827_corr.0

Metadata
Parameter Value
File Name DRIFTS_pot012_13Csoy_wk10_root_250827_corr.0
Timestamp 2025-08-27 18:52:52 GMT-7
Max Y 0.797225415706635
Min Y -0.00604866677895188
Aperture Setting 6 mm
Scanner Velocity 10.0S
Result Spectrum AB
Resolution 4
Sample Scans 400
End Frequency 400
Start Frequency 4000
Duration 364.560791015625
Experiment (method) SALK_soils_method.xpm

Spectrum 3 : DRIFTS_pot029_13Cwheat_wk10_root_250827_corr.0

Metadata
Parameter Value
File Name DRIFTS_pot029_13Cwheat_wk10_root_250827_corr.0
Timestamp 2025-08-27 18:53:45 GMT-7
Max Y 0.722125887870789
Min Y -0.00508248526602983
Aperture Setting 6 mm
Scanner Velocity 10.0S
Result Spectrum AB
Resolution 4
Sample Scans 400
End Frequency 400
Start Frequency 4000
Duration 364.559814453125
Experiment (method) SALK_soils_method.xpm

Spectrum 4 : DRIFTS_pot029_13Cwheat_wk40_root_250827_corr.0

Metadata
Parameter Value
File Name DRIFTS_pot029_13Cwheat_wk40_root_250827_corr.0
Timestamp 2025-08-27 18:54:46 GMT-7
Max Y 0.828895390033722
Min Y -0.00468211621046066
Aperture Setting 6 mm
Scanner Velocity 10.0S
Result Spectrum AB
Resolution 4
Sample Scans 400
End Frequency 400
Start Frequency 4000
Duration 364.560791015625
Experiment (method) SALK_soils_method.xpm

Spectrum 5 : DRIFTS_pot030_13Csoy_wk10_root_250725_corr.0

Metadata
Parameter Value
File Name DRIFTS_pot030_13Csoy_wk10_root_250725_corr.0
Timestamp 2025-07-25 20:21:37 GMT-7
Max Y 0.84355878829956
Min Y -0.00326710939407349
Aperture Setting 6 mm
Scanner Velocity 10.0S
Result Spectrum AB
Resolution 4
Sample Scans 400
End Frequency 400
Start Frequency 4000
Duration 364.56689453125
Experiment (method) SALK_soils_method.xpm

Spectrum 6 : DRIFTS_pot030_13Csoy_wk40_root_250827_corr.0

Metadata
Parameter Value
File Name DRIFTS_pot030_13Csoy_wk40_root_250827_corr.0
Timestamp 2025-08-27 18:54:58 GMT-7
Max Y 0.721331775188446
Min Y -0.00639449199661613
Aperture Setting 6 mm
Scanner Velocity 10.0S
Result Spectrum AB
Resolution 4
Sample Scans 400
End Frequency 400
Start Frequency 4000
Duration 364.559814453125
Experiment (method) SALK_soils_method.xpm

Spectrum 7 : DRIFTS_pot032_13Cwheat_wk0_root_250725_corr.0

Metadata
Parameter Value
File Name DRIFTS_pot032_13Cwheat_wk0_root_250725_corr.0
Timestamp 2025-07-25 20:22:45 GMT-7
Max Y 0.909599244594574
Min Y -0.00415003532543778
Aperture Setting 6 mm
Scanner Velocity 10.0S
Result Spectrum AB
Resolution 4
Sample Scans 400
End Frequency 400
Start Frequency 4000
Duration 364.56982421875
Experiment (method) SALK_soils_method.xpm

Spectrum 8 : DRIFTS_pot047_13Crice_wk0_root_250725_corr.0

Metadata
Parameter Value
File Name DRIFTS_pot047_13Crice_wk0_root_250725_corr.0
Timestamp 2025-07-25 20:23:56 GMT-7
Max Y 0.712926685810089
Min Y -0.00133463030215353
Aperture Setting 6 mm
Scanner Velocity 10.0S
Result Spectrum AB
Resolution 4
Sample Scans 400
End Frequency 400
Start Frequency 4000
Duration 364.56591796875
Experiment (method) SALK_soils_method.xpm

Spectrum 9 : DRIFTS_pot050_13Csoy_wk0_root_250827_corr.0

Metadata
Parameter Value
File Name DRIFTS_pot050_13Csoy_wk0_root_250827_corr.0
Timestamp 2025-08-27 18:55:10 GMT-7
Max Y 0.772085309028625
Min Y -0.00875790882855654
Aperture Setting 6 mm
Scanner Velocity 10.0S
Result Spectrum AB
Resolution 4
Sample Scans 400
End Frequency 400
Start Frequency 4000
Duration 364.560791015625
Experiment (method) SALK_soils_method.xpm

Spectrum 10 : DRIFTS_pot052_13CnoPlant_wk30_root_250919_corr.0

Metadata
Parameter Value
File Name DRIFTS_pot052_13CnoPlant_wk30_root_250919_corr.0
Timestamp 2025-09-19 18:16:15 GMT-7
Max Y 0.77551531791687
Min Y -0.005828230176121
Aperture Setting 6 mm
Scanner Velocity 10.0S
Result Spectrum AB
Resolution 4
Sample Scans 400
End Frequency 400
Start Frequency 4000
Duration 364.392822265625
Experiment (method) SALK_soils_method.xpm

Spectrum 11 : DRIFTS_pot052_13CnoPlant_wk40_root_250919_corr.0

Metadata
Parameter Value
File Name DRIFTS_pot052_13CnoPlant_wk40_root_250919_corr.0
Timestamp 2025-09-19 18:16:20 GMT-7
Max Y 0.813514113426208
Min Y -0.004625812638551
Aperture Setting 6 mm
Scanner Velocity 10.0S
Result Spectrum AB
Resolution 4
Sample Scans 400
End Frequency 400
Start Frequency 4000
Duration 364.393798828125
Experiment (method) SALK_soils_method.xpm

Spectrum 12 : DRIFTS_pot053_13Cwheat_wk10_root_250725_corr.0

Metadata
Parameter Value
File Name DRIFTS_pot053_13Cwheat_wk10_root_250725_corr.0
Timestamp 2025-07-25 20:24:39 GMT-7
Max Y 0.997280716896057
Min Y -0.00962971057742834
Aperture Setting 6 mm
Scanner Velocity 10.0S
Result Spectrum AB
Resolution 4
Sample Scans 400
End Frequency 400
Start Frequency 4000
Duration 364.56591796875
Experiment (method) SALK_soils_method.xpm

Spectrum 13 : DRIFTS_pot053_13Cwheat_wk40_root_250919_corr.0

Metadata
Parameter Value
File Name DRIFTS_pot053_13Cwheat_wk40_root_250919_corr.0
Timestamp 2025-09-19 15:59:23 GMT-7
Max Y 0.797653913497925
Min Y -0.00369235360994935
Aperture Setting 6 mm
Scanner Velocity 10.0S
Result Spectrum AB
Resolution 4
Sample Scans 400
End Frequency 400
Start Frequency 4000
Duration 364.389892578125
Experiment (method) SALK_soils_method.xpm

Spectrum 14 : DRIFTS_pot055_13Crice_wk10_root_250725_corr.0

Metadata
Parameter Value
File Name DRIFTS_pot055_13Crice_wk10_root_250725_corr.0
Timestamp 2025-07-25 20:26:35 GMT-7
Max Y 0.816974341869354
Min Y -0.00123318459372967
Aperture Setting 6 mm
Scanner Velocity 10.0S
Result Spectrum AB
Resolution 4
Sample Scans 400
End Frequency 400
Start Frequency 4000
Duration 364.56689453125
Experiment (method) SALK_soils_method.xpm

Spectrum 15 : DRIFTS_pot055_13Crice_wk40_root_250827_corr.0

Metadata
Parameter Value
File Name DRIFTS_pot055_13Crice_wk40_root_250827_corr.0
Timestamp 2025-08-27 18:55:22 GMT-7
Max Y 0.852853655815125
Min Y -0.00635257223621011
Aperture Setting 6 mm
Scanner Velocity 10.0S
Result Spectrum AB
Resolution 4
Sample Scans 400
End Frequency 400
Start Frequency 4000
Duration 364.56396484375
Experiment (method) SALK_soils_method.xpm

Spectrum 16 : DRIFTS_pot055qmark_13Crice_wk10_root_250725_corr.0

Metadata
Parameter Value
File Name DRIFTS_pot055qmark_13Crice_wk10_root_250725_corr.0
Timestamp 2025-07-25 20:25:51 GMT-7
Max Y 0.907369911670685
Min Y -0.00234174402430654
Aperture Setting 6 mm
Scanner Velocity 10.0S
Result Spectrum AB
Resolution 4
Sample Scans 400
End Frequency 400
Start Frequency 4000
Duration 364.56591796875
Experiment (method) SALK_soils_method.xpm

Spectrum 17 : DRIFTS_pot056_12Csoy_wk10_root_250919_corr.0

Metadata
Parameter Value
File Name DRIFTS_pot056_12Csoy_wk10_root_250919_corr.0
Timestamp 2025-10-07 18:03:40 GMT-7
Max Y 0.615259230136871
Min Y -0.0116841280832887
Aperture Setting 6 mm
Scanner Velocity 10.0S
Result Spectrum AB
Resolution 4
Sample Scans 400
End Frequency 400
Start Frequency 4000
Duration 364.393798828125
Experiment (method) SALK_soils_method.xpm

Spectrum 18 : DRIFTS_pot062_13Csoy_wk0_root_250725_corr.0

Metadata
Parameter Value
File Name DRIFTS_pot062_13Csoy_wk0_root_250725_corr.0
Timestamp 2025-07-25 20:27:28 GMT-7
Max Y 0.687717080116272
Min Y -0.00538174575194716
Aperture Setting 6 mm
Scanner Velocity 10.0S
Result Spectrum AB
Resolution 4
Sample Scans 400
End Frequency 400
Start Frequency 4000
Duration 364.56884765625
Experiment (method) SALK_soils_method.xpm

Spectrum 19 : DRIFTS_pot079_13Crice_wk0_root_250725_corr.0

Metadata
Parameter Value
File Name DRIFTS_pot079_13Crice_wk0_root_250725_corr.0
Timestamp 2025-07-25 20:28:00 GMT-7
Max Y 0.816869914531708
Min Y -0.00257659493945539
Aperture Setting 6 mm
Scanner Velocity 10.0S
Result Spectrum AB
Resolution 4
Sample Scans 400
End Frequency 400
Start Frequency 4000
Duration 364.56787109375
Experiment (method) SALK_soils_method.xpm

Spectrum 20 : DRIFTS_pot080_13Csoy_wk10_root_250725_corr.0

Metadata
Parameter Value
File Name DRIFTS_pot080_13Csoy_wk10_root_250725_corr.0
Timestamp 2025-07-25 20:28:56 GMT-7
Max Y 0.77625048160553
Min Y -0.00814440380781889
Aperture Setting 6 mm
Scanner Velocity 10.0S
Result Spectrum AB
Resolution 4
Sample Scans 400
End Frequency 400
Start Frequency 4000
Duration 364.56689453125
Experiment (method) SALK_soils_method.xpm

Spectrum 21 : DRIFTS_pot080_13Csoy_wk40_root_250919_corr.0

Metadata
Parameter Value
File Name DRIFTS_pot080_13Csoy_wk40_root_250919_corr.0
Timestamp 2025-09-19 16:42:23 GMT-7
Max Y 0.764196336269379
Min Y -0.00511131621897221
Aperture Setting 6 mm
Scanner Velocity 10.0S
Result Spectrum AB
Resolution 4
Sample Scans 400
End Frequency 400
Start Frequency 4000
Duration 364.391845703125
Experiment (method) SALK_soils_method.xpm

Spectrum 22 : DRIFTS_pot086_12Csoy_wk0_root_250919_corr.0

Metadata
Parameter Value
File Name DRIFTS_pot086_12Csoy_wk0_root_250919_corr.0
Timestamp 2025-10-07 18:04:00 GMT-7
Max Y 0.540434181690216
Min Y -0.0042975265532732
Aperture Setting 6 mm
Scanner Velocity 10.0S
Result Spectrum AB
Resolution 4
Sample Scans 400
End Frequency 400
Start Frequency 4000
Duration 364.386962890625
Experiment (method) SALK_soils_method.xpm

Spectrum 23 : DRIFTS_pot087_13Crice_wk10_root_250827_corr.0

Metadata
Parameter Value
File Name DRIFTS_pot087_13Crice_wk10_root_250827_corr.0
Timestamp 2025-08-27 18:55:34 GMT-7
Max Y 0.699629366397858
Min Y -0.00116329535376281
Aperture Setting 6 mm
Scanner Velocity 10.0S
Result Spectrum AB
Resolution 4
Sample Scans 400
End Frequency 400
Start Frequency 4000
Duration 364.560791015625
Experiment (method) SALK_soils_method.xpm

Spectrum 24 : DRIFTS_pot087_13Crice_wk40_root_250919_corr.0

Metadata
Parameter Value
File Name DRIFTS_pot087_13Crice_wk40_root_250919_corr.0
Timestamp 2025-09-19 17:31:48 GMT-7
Max Y 0.879431128501892
Min Y -0.0040658856742084
Aperture Setting 6 mm
Scanner Velocity 10.0S
Result Spectrum AB
Resolution 4
Sample Scans 400
End Frequency 400
Start Frequency 4000
Duration 364.392822265625
Experiment (method) SALK_soils_method.xpm

Spectrum 25 : DRIFTS_pot094_13Csoy_wk10_root_250725_corr.0

Metadata
Parameter Value
File Name DRIFTS_pot094_13Csoy_wk10_root_250725_corr.0
Timestamp 2025-07-25 20:29:09 GMT-7
Max Y 1.27848649024963
Min Y -0.00875786785036325
Aperture Setting 6 mm
Scanner Velocity 10.0S
Result Spectrum AB
Resolution 4
Sample Scans 400
End Frequency 400
Start Frequency 4000
Duration 364.56689453125
Experiment (method) SALK_soils_method.xpm

Spectrum 26 : DRIFTS_pot094_13Csoy_wk10_soil_250630_corr.0

Metadata
Parameter Value
File Name DRIFTS_pot094_13Csoy_wk10_soil_250630_corr.0
Timestamp 2025-07-25 20:46:03 GMT-7
Max Y 0.821687042713165
Min Y -0.00153941253665835
Aperture Setting 6 mm
Scanner Velocity 10.0S
Result Spectrum AB
Resolution 4
Sample Scans 400
End Frequency 400
Start Frequency 4000
Duration 364.454833984375
Experiment (method) SALK_soils_method.xpm

Spectrum 27 : DRIFTS_pot094_13Csoy_wk40_root_250919_corr.0

Metadata
Parameter Value
File Name DRIFTS_pot094_13Csoy_wk40_root_250919_corr.0
Timestamp 2025-09-19 16:42:30 GMT-7
Max Y 0.825236082077026
Min Y -0.00458295457065105
Aperture Setting 6 mm
Scanner Velocity 10.0S
Result Spectrum AB
Resolution 4
Sample Scans 400
End Frequency 400
Start Frequency 4000
Duration 364.391845703125
Experiment (method) SALK_soils_method.xpm

Spectrum 28 : DRIFTS_pot103_13Crice_wk0_root_recNMR_250725_corr.0

Metadata
Parameter Value
File Name DRIFTS_pot103_13Crice_wk0_root_recNMR_250725_corr.0
Timestamp 2025-07-25 20:30:32 GMT-7
Max Y 0.836920917034149
Min Y -0.00138377456460148
Aperture Setting 6 mm
Scanner Velocity 10.0S
Result Spectrum AB
Resolution 4
Sample Scans 400
End Frequency 400
Start Frequency 4000
Duration 364.56982421875
Experiment (method) SALK_soils_method.xpm

Spectrum 29 : DRIFTS_pot103_13Crice_wk0_root_vial1_250725_corr.0

Metadata
Parameter Value
File Name DRIFTS_pot103_13Crice_wk0_root_vial1_250725_corr.0
Timestamp 2025-07-25 20:30:55 GMT-7
Max Y 0.826825261116028
Min Y -0.000596170837525278
Aperture Setting 6 mm
Scanner Velocity 10.0S
Result Spectrum AB
Resolution 4
Sample Scans 400
End Frequency 400
Start Frequency 4000
Duration 364.56787109375
Experiment (method) SALK_soils_method.xpm

Spectrum 30 : DRIFTS_pot103_13Crice_wk0_root_vial2_250725_corr.0

Metadata
Parameter Value
File Name DRIFTS_pot103_13Crice_wk0_root_vial2_250725_corr.0
Timestamp 2025-07-25 20:31:26 GMT-7
Max Y 0.984898567199707
Min Y -0.00565983727574348
Aperture Setting 6 mm
Scanner Velocity 10.0S
Result Spectrum AB
Resolution 4
Sample Scans 400
End Frequency 400
Start Frequency 4000
Duration 364.56591796875
Experiment (method) SALK_soils_method.xpm

Spectrum 31 : DRIFTS_pot103_13Crice_wk0_root_vial3_250725_corr.0

Metadata
Parameter Value
File Name DRIFTS_pot103_13Crice_wk0_root_vial3_250725_corr.0
Timestamp 2025-07-25 20:31:46 GMT-7
Max Y 0.983739078044891
Min Y -0.0026757272426039
Aperture Setting 6 mm
Scanner Velocity 10.0S
Result Spectrum AB
Resolution 4
Sample Scans 400
End Frequency 400
Start Frequency 4000
Duration 364.56591796875
Experiment (method) SALK_soils_method.xpm

Spectrum 32 : DRIFTS_pot107_12Crice_wk0_root_really13_250725_corr.0

Metadata
Parameter Value
File Name DRIFTS_pot107_12Crice_wk0_root_really13_250725_corr.0
Timestamp 2025-07-25 20:32:00 GMT-7
Max Y 1.03206670284271
Min Y -0.00172662467230111
Aperture Setting 6 mm
Scanner Velocity 10.0S
Result Spectrum AB
Resolution 4
Sample Scans 400
End Frequency 400
Start Frequency 4000
Duration 364.56591796875
Experiment (method) SALK_soils_method.xpm

Spectrum 33 : DRIFTS_pot109_13Cwheat_wk40_root_250919_corr.0

Metadata
Parameter Value
File Name DRIFTS_pot109_13Cwheat_wk40_root_250919_corr.0
Timestamp 2025-09-19 15:59:28 GMT-7
Max Y 0.75251305103302
Min Y -0.00450036907568574
Aperture Setting 6 mm
Scanner Velocity 10.0S
Result Spectrum AB
Resolution 4
Sample Scans 400
End Frequency 400
Start Frequency 4000
Duration 364.386962890625
Experiment (method) SALK_soils_method.xpm

Spectrum 34 : DRIFTS_pot119_13Crice_wk10_root_250827_corr.0

Metadata
Parameter Value
File Name DRIFTS_pot119_13Crice_wk10_root_250827_corr.0
Timestamp 2025-08-27 18:55:49 GMT-7
Max Y 0.808935940265656
Min Y -0.0116460844874382
Aperture Setting 6 mm
Scanner Velocity 10.0S
Result Spectrum AB
Resolution 4
Sample Scans 400
End Frequency 400
Start Frequency 4000
Duration 364.561767578125
Experiment (method) SALK_soils_method.xpm

Spectrum 35 : DRIFTS_pot119_13Crice_wk40_root_250919_corr.0

Metadata
Parameter Value
File Name DRIFTS_pot119_13Crice_wk40_root_250919_corr.0
Timestamp 2025-09-19 17:31:43 GMT-7
Max Y 0.954932034015656
Min Y -0.00412484398111701
Aperture Setting 6 mm
Scanner Velocity 10.0S
Result Spectrum AB
Resolution 4
Sample Scans 400
End Frequency 400
Start Frequency 4000
Duration 364.390869140625
Experiment (method) SALK_soils_method.xpm

Stacking multiple spectra

Stacking re-runs

This code looks for files with the same sample number and stacks their spectra, to check whether reruns are consistent.

knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>",
  echo = TRUE,
  results = "markup",
  include = TRUE
)
library(opusreader2)
library(RColorBrewer)



data_test <- read_opus(dsn = corr_spectra)

# Helper to extract sample number from filename (e.g., "pot094")
extract_sample_number <- function(filename) {
  m <- regexpr("[a-z]{3}[0-9]+", filename)
  if (m[1] != -1) {
    regmatches(filename, m)
  } else {
    NA
  }
}

# Helper to extract sample type (e.g.,"soil", "root")
extract_sample_type <- function(filename) {
  m <- regexpr("soil|root", filename, ignore.case = TRUE)
  if (m[1] != -1) {
    tolower(regmatches(filename, m))
  } else {
    NA
  }
}

# Helper to extract timepoint (e.g., "wk0", "wk40")
extract_timepoint <- function(filename) {
  m <- regexpr("wk[0-9]+", filename, ignore.case = TRUE)
  if (m[1] != -1) {
    tolower(regmatches(filename, m))
  } else {
    NA
  }
}

# Group files by sample number, sample type, and timepoint
sample_numbers <- sapply(names(data_test), extract_sample_number)
sample_types <- sapply(names(data_test), extract_sample_type)
timepoints <- sapply(names(data_test), extract_timepoint)

# Create unique combinations of sample number, sample type, and timepoint
sample_combinations <- paste(sample_numbers, sample_types, timepoints, sep = "_")
unique_combinations <- unique(sample_combinations[!is.na(sample_numbers)
                                                  & !is.na(sample_types)
                                                  & !is.na(timepoints)])

# Plot stacked spectra for each unique combination
library(RColorBrewer)
for (combination in unique_combinations) {
  idx <- which(sample_combinations == combination)
  if (length(idx) > 1) {
    # Extract sample number, type, and timepoint from combination
    parts <- strsplit(combination, "_")[[1]]
    sample_num <- parts[1]
    sample_type <- parts[2]
    timepoint <- parts[3]

    spectra <- data_test[idx]
    colors <- brewer.pal(min(length(spectra), 8), "Set1")
    plot(NULL, xlim = rev(range(spectra[[1]]$ab$wavenumbers)),
         ylim = range(sapply(spectra, function(x) x$ab$data), na.rm = TRUE),
         xlab = "Wavenumber (cm⁻¹)", ylab = "Absorbance",
         main = paste("Stacked spectra for", sample_num, "-", sample_type, "-", timepoint),
         bty = "l")
    for (i in seq_along(spectra)) {
      ab_data <- spectra[[i]]$ab
      if (!is.null(ab_data) && !is.null(ab_data$wavenumbers) &&
            !is.null(ab_data$data)) {
        lines(ab_data$wavenumbers, ab_data$data, col = colors[i], lwd = 2)
      }
    }
    legend("topright",
           inset = c(-0.05, -0.02),
           legend = names(spectra),
           col = colors[seq_along(spectra)],
           lwd = 2,
           xpd = TRUE,
           bty = "n")
  }
}

Stacking by crop & time

This code stacks spectra of the same crop and timepoint.

knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>",
  echo = TRUE,
  results = "markup",
  include = TRUE
)
# Helper functions to extract crop and timepoint from filename
extract_crop <- function(filename) {
  m <- regexpr("rice|wheat|soy", filename, ignore.case = TRUE)
  if (m[1] != -1) tolower(regmatches(filename, m)) else NA
}
extract_time <- function(filename) {
  m <- regexpr("wk[0-9]+", filename, ignore.case = TRUE)
  if (m[1] != -1) tolower(regmatches(filename, m)) else NA
}
extract_sample_type <- function(filename) {
  m <- regexpr("root|soil", filename, ignore.case = TRUE)
  if (m[1] != -1) {
    tolower(regmatches(filename, m))
  } else {
    NA
  }
}
# Get crop and time for each file
crops <- sapply(names(data_test), extract_crop)
times <- sapply(names(data_test), extract_time)
types <- sapply(names(data_test), extract_sample_type)

# Unique crop-time combinations
combos <- na.omit(unique(paste(crops, times, types, sep = "_")))

library(RColorBrewer)
for (combo in combos) {
  idx <- which(paste(crops, times, types, sep = "_") == combo)
  if (length(idx) > 1) {
    spectra <- data_test[idx]
    colors <- brewer.pal(min(length(spectra), 8), "Set1")
    plot(NULL, xlim = rev(range(spectra[[1]]$ab$wavenumbers)),
         ylim = range(sapply(spectra, function(x) x$ab$data), na.rm = TRUE),
         xlab = "Wavenumber (cm⁻¹)", ylab = "Absorbance",
         main = paste("Stacked spectra for", combo),
         bty = "l")
    for (i in seq_along(spectra)) {
      ab_data <- spectra[[i]]$ab
      if (!is.null(ab_data) &&
            !is.null(ab_data$wavenumbers)
          && !is.null(ab_data$data)) {
        lines(ab_data$wavenumbers, ab_data$data, col = colors[i], lwd = 2)
      }
    }
    legend("topright",
           inset = c(-0.05, -0.02),
           legend = names(spectra),
           col = colors[seq_along(spectra)],
           lwd = 2,
           xpd = TRUE,
           bty = "n")
  }
}